Klasyfikacja¶

Autorzy¶

Adam Dohojda, Kacper Kiereś, Dawid Koceniak, Wiktoria Stęczna

Streszczenie¶

Praca przedstawia próbę analizy i klasyfikacji obserwacji z wybranego repozytorium Machine Learningu dostępne tutaj. Proces przetwarzania danych zawiera wstępną analizę danych w postaci obliczenia statystyk opisowych, standaryzacji zmiennych oraz wykorzystanie modeli klasyfikacyjnych (regresji liniowej, SVM, KNN, sieci neuronowych, lasów losowych, drzew decyzyjnych, LDA, QDA) by przyporządkować obseracje do konkretnej kategorii. Na koniec robimy podsumowanie naszych wyników w postaci zestawienia tabelarycznego poszczególnych wskaźników określających dokładność dopasowania kategorii naszych obserwacji.

Przedmiot badania¶

Przedmiotem badania są dwie odmiany ryżu uprawianych w Turcji - Osmanick oraz Cammeo. Każdy z tych gatunków wyróżnia się swoimi właściwościami. Wykonano 3810 zdjęć ziaren ryżu obu gatunków, przetworzono je i dokonano wnioskowania o cechach. Uzyskano 8 cech morfologicznych, które zapisano w repozytorium.

Cel i zakres badania¶

Mając do dyspozycji dane dotyczące dwóch gatunków ryżu, chcieliśmy dokonać klasyfikacji, które należą do gatunku $Cammeo$, a które do $Osmancik$.

Słowa kluczowe¶

  • klastrowanie
  • potok
  • próby testowe i uczące
  • wskaźniki wydajności

Zmienne wybrane do analizy¶

area - liczba pikseli na granicach ziarna ryżu
perimeter - obwód, obliczony na podstawie między pikselami wokół granic ziarna ryżu
Major_Axis_Length - najdłuższa linia, jaką można narysować na ziarnie ryżu, tj. odległość osi głównej
Minor_Axis_Length - najkrótsza linia, jaką można narysować na ziarnie ryżu, tj. odległość osi głównej
Eccentricity - mierzy on, jak okrągła jest elipsa, która ma te same momenty co ziarno ryżu
Convex_area - zwraca liczbę pikseli najmniejszej wypukłej powłoki obszaru utworzonego przez ziarno ryżu
Extent - Zwraca stosunek obszaru utworzonego przez ziarno ryżu do obwiedni
Class - Cammeo oraz Osmancik

Wstępne analiza danych¶

Import bibliotek¶
In [ ]:
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.svm import SVC
from sklearn.linear_model import Perceptron
from sklearn.preprocessing import LabelEncoder
from matplotlib.colors import ListedColormap
from sklearn.model_selection import StratifiedKFold
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score
import seaborn as sns
from scipy.stats import skew, kurtosis
from sklearn.metrics import confusion_matrix
from sklearn.metrics import roc_curve, auc
from numpy import interp
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score, f1_score
from sklearn.metrics import matthews_corrcoef
from sklearn.metrics import make_scorer
from sklearn.tree import DecisionTreeClassifier
from scipy.stats import randint
from sklearn.neural_network import MLPClassifier
from sklearn.pipeline import Pipeline
from sklearn.model_selection import validation_curve
from sklearn.model_selection import learning_curve
from sklearn.model_selection import RandomizedSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis as QDA
Wczytywanie danych¶
In [ ]:
df = pd.read_excel('rice.xlsx').set_index('Class')
Obsługa braków danych¶
In [ ]:
np.sum(np.isnan(df), axis=0)
Out[ ]:
Area                 0
Perimeter            0
Major_Axis_Length    0
Minor_Axis_Length    0
Eccentricity         0
Convex_Area          0
Extent               0
dtype: int64

Stwierdzamy brak pustych wartości

Wizualizacja zmiennych, wraz z ich szacowanymi gęstościami¶

W tym kroku zwizualizujemy wszystkie zmienne, chcąc otrzymać histogramy gęstości (tak więc oś pionowa przedstawia częstości, zamiast liczby występujących wartości zmiennej). Dodatkowo w celu pogłębionego zrozumienia skorzystaliśmy z metody kdeplot z pakietu SeaBorn w celu nakreślenia estymowanej krzywej gęstości. Więcej o zaimplementowanej metodzie można poczytać tutaj.

In [ ]:
plt.style.use('fast')
fig, axs = plt.subplots(nrows=4, ncols=2, figsize=(12,10))
plt.subplots_adjust(hspace=0.5)
for ax, col in zip(axs.flat, df.columns):
    ax.hist(df[col], bins=10, density=True)
    sns.kdeplot(df[col], ax=ax)
    ax.grid(True)
    ax.set_title(col)
    ax.set_xlabel('')
Statystyki opisowe¶

W tej części policzymy i zinterpretujemy statystyki opisowe naszego zbioru danych. Do podstawowych statystyk liczonych przez pakiet Pandas dodaliśmy obliczoną skośność i kurtozę.

In [ ]:
skewnesses = skew(df, axis=0)
kurtosises = kurtosis(df, axis=0)
stats = pd.concat([df.describe(), pd.DataFrame({'skewness':skewnesses, 'kurtosis':kurtosises}, index=df.columns).T]).T
stats['coef_of_var'] = stats['std'] / stats['mean']
stats
Out[ ]:
count mean std min 25% 50% 75% max skewness kurtosis coef_of_var
Area 3810.0 12667.727559 1732.367706 7551.000000 11370.500000 12421.500000 13950.000000 18913.000000 0.325030 -0.432091 0.136754
Perimeter 3810.0 454.239180 35.597081 359.100006 426.144752 448.852493 483.683746 548.445984 0.221275 -0.840715 0.078366
Major_Axis_Length 3810.0 188.776222 17.448679 145.264465 174.353855 185.810059 203.550438 239.010498 0.260140 -0.952128 0.092430
Minor_Axis_Length 3810.0 86.313750 5.729817 59.532406 82.731695 86.434647 90.143677 107.542450 -0.134844 0.559801 0.066384
Eccentricity 3810.0 0.886871 0.020818 0.777233 0.872402 0.889050 0.902588 0.948007 -0.449072 0.069405 0.023473
Convex_Area 3810.0 12952.496850 1776.972042 7723.000000 11626.250000 12706.500000 14284.000000 19099.000000 0.319656 -0.466788 0.137191
Extent 3810.0 0.661934 0.077239 0.497413 0.598862 0.645361 0.726562 0.861050 0.343684 -1.030324 0.116687

Modele klasyfikacyjne¶

Przygotowywanie danych do przeprowadzenia klasyfikacji¶

Załadowanie danych¶
In [ ]:
df = pd.read_excel('rice.xlsx', header=None)
X = df.loc[1:, :6].values
y = df.loc[1:, 7].values

Używając obiektu LabelEncoder, przekształcamy etykiety klas na liczby całkowite

In [ ]:
le = LabelEncoder()
y = le.fit_transform(y)
print("Class labels: ", le.classes_)
Class labels:  ['Cammeo' 'Osmancik']
In [ ]:
le.transform(['Cammeo', 'Osmancik'])
Out[ ]:
array([0, 1])
Dzielenie zbioru danych na próbę treningową (80% danych) i próbę testową (20% danych).¶
In [ ]:
X_train, X_test, y_train, y_test = \
    train_test_split(X, y, test_size = 0.2, 
                     stratify = y,
                     random_state = 0)
In [ ]:
print('Labels counts in y: ', np.bincount(y))
print('Labels counts in y_train: ', np.bincount(y_train))
print('Labels counts in y_test: ', np.bincount(y_test))
Labels counts in y:  [1630 2180]
Labels counts in y_train:  [1304 1744]
Labels counts in y_test:  [326 436]
Standaryzacja zmiennych¶
In [ ]:
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)
Tworzenie funckji plot_decision_regions()¶

Implementacja funkcji do wizualizacji granic decyzyjnych dwuwymiarowych danych

In [ ]:
def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):
    # Setup marker generator and color map
    markers = ('o', 's', '^', 'v', '<', '>', 'D', 'P', 'X', '*')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan', 'orange', 'purple', 'pink', 'yellow', 'black')
    cmap = ListedColormap(colors[:len(np.unique(y))])
    # Plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    lab = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    lab = lab.reshape(xx1.shape)
    plt.contourf(xx1, xx2, lab, alpha=0.3, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())
    # Plot class examples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0],
                    y=X[y == cl, 1],
                    alpha=0.8,
                    c=colors[idx],
                    marker=markers[idx],
                    label=f'Class {cl}',
                    edgecolor='black')

Funkcja rysuje obszar decyzyjny na podstawie danych treningowych i klasyfikatora, a także wyświetla przykłady klas.

Definiujemy liczbę kolorów i znaczników oraz tworzymy mapę kolorów z listy kolorów za pomocą ListedColormap.

Tworzymy siatkę punktów xx1 i xx2 na podstawie minimalnych i maksymalnych wartości cech, a następnie przewidujemy klasy dla każdego punktu na siatce za pomocą modelu klasyfikatora classifier.predict. Wynikowy konturowy wykres przedstawia obszar decyzyjny, gdzie różne kolory odpowiadają różnym klasom, a punkty treningowe są również wyświetlane na wykresie, reprezentując różne klasy.

Dodatkowo, używając różnych markerów markers i kolorów colors, funkcja oznacza punkty dla różnych klas na wykresie, co pozwala wizualnie rozróżnić klasy.

plot_decision_regions będzie przez nas wykorzystywany, kiedy wyłonimy w dalszej części projektu modele, o jak najlepszej dokładności dopasowania do danych.

Regresja logistyczna¶

Inicjalizacja tranformatora PCA i estymatora regresji logistycznej

In [ ]:
pca = PCA(n_components = 2)
lr = LogisticRegression(multi_class = 'ovr',
                        random_state = 1,
                        solver = 'lbfgs')

Redukcja wymiarów

In [ ]:
X_train_pca = pca.fit_transform(X_train_std)
X_test_pca = pca.transform(X_test_std)

Dopasowywanie modelu regresji logistycznej do zredukowanego zbioru danych

In [ ]:
lr.fit(X_train_pca, y_train)
plot_decision_regions(X_train_pca, y_train, classifier = lr)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc = 'best')
plt.tight_layout()
plt.show()
In [ ]:
plot_decision_regions(X_test_pca, y_test, classifier = lr)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc = 'best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu regresji logistycznej¶

Łączenie transformatorów i estymatorów w potoku¶

Normalizujemy kolumny w zbiorze danych za pomocą StandardScaler, kompresujemy dane do dwuwymiarowej podprzestrzeni za pomocą PCA. a następnie przekazujemy je do klasyfikatora regresji logistycznej LogisticRegression, za pomocą funkcji make_pipeline.

In [ ]:
pipe_lr = make_pipeline(StandardScaler(),
                        PCA(n_components = 2),
                        LogisticRegression())
pipe_lr.fit(X_train, y_train)
y_pred = pipe_lr.predict(X_test)
test_acc_lr = pipe_lr.score(X_test, y_test)
print(f'Test accuracy: {test_acc_lr: .3f}')
Test accuracy:  0.923

Funkcja make_pipeline pobiera dowolną liczbę transformatorów scikit-learn, a następnie estymator scikit-learn, który implementuje metody fit oraz predict.

W powyższym zapisie, dostarczyliśmy dwa transformatory scikit-learn - StandardScaler i PCA oraz estymator LogisticRegression jako dane wejściowe do funkcji make_pipeline, która konstruuje obiekt scikit-learn Pipeline z tych obiektów.

Na końcu zwracamy prognozę na podstawie przekształconych danych.

Macierz pomyłek¶

In [ ]:
pipe_lr.fit(X_train, y_train)
y_pred = pipe_lr.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[300  26]
 [ 33 403]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_lr = cross_val_score(estimator = pipe_lr,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_lr}')
print(f'CV accuracy scores: {np.mean(scores_lr):.3f}'
      f' +/- {np.std(scores_lr):.3f}')
CV accuracy scores: [0.92786885 0.93770492 0.90819672 0.91803279 0.9147541  0.94754098
 0.92131148 0.9147541  0.92763158 0.91447368]
CV accuracy scores: 0.923 +/- 0.011

Optymalizacja dokładności (precision), wyniku wycofania (recall score), wyniku f1 (F1 score), oraz współczynnika mathhews dla modelu klasyfikacji¶

Zarówno błąd predykcji (ERR), jak i dokładność (ACC) dostarczają ogólnych informacji o tym, ile przykładów zostało błędnie sklasyfikowanych. Błąd można rozumieć jako sumę wszystkich fałszywych przewidywań podzieloną przez liczbę przez liczbę całkowitych przewidywań, a dokładność jest obliczana jako suma poprawnych przewidywań podzielona przez całkowitą liczbę przewidywań. podzielona odpowiednio przez całkowitą liczbę przewidywań: $$ ERR = \frac{FP + FN}{FP + FN + TP + TN}$$

Dokładność predykcji można następnie obliczyć bezpośrednio na podstawie błędu: $$ ACC = \frac{TP + TN}{FP + FN + TP + TN} = 1 - ERR $$

Współczynnik wyników prawdziwie pozytywnych (True Positive Rate) i współczynnik wyników fałszywie pozytywnych (False Positive Rate) to wskaźniki wydajności, które są szczególnie przydatne w przypadku problemów z niezrównoważonymi klasami: $$ FPR = \frac{FP}{N} = \frac{FP}{FP+TN}$$ $$ TPR = \frac{TP}{P} = \frac{TP}{FN + TP}$$

Wskaźniki wydajności precyzji (PRE) i wycofania (REC) są powiązane z tymi wskaźnikami TP i TN, i w rzeczywistości REC jest synonimem TPR:

$$ REC = TPR = \frac{TP}{P} = \frac{TP}{FN + TP} $$

Innymi słowy, przywołanie określa ilościowo, ile odpowiednich rekordów (pozytywnych) zostało przechwyconych jako takie (prawdziwe pozytywne). Precyzja określa, ile rekordów przewidywanych jako istotne (suma prawdziwych i fałszywych wyników pozytywnych) jest faktycznie istotnych (prawdziwe wyniki pozytywne):

$$ PRE = \frac{TP}{TP + FP}$$

Aby zrównoważyć wady i zalety optymalizacji PRE i REC, stosuje się średnią harmoniczną PRE i REC tak zwany wynik F1:

$$ F1 = 2 \frac{PRE \times REC}{PRE + REC}$$

Miarą podsumowującą macierz pomyłek jest MCC. MCC oblicza się w następujący sposób: $$ MCC = \frac{TP \times TN - FP \times FN}{\sqrt{(TP + FP)(TP + FN)(TP + FP)(TN + FN)}}$$

W przeciwieństwie do PRE, REC i wyniku F1, MCC mieści się w zakresie od -1 do 1 i uwzględnia wszystkie elementy macierzy pomyłek - na przykład wynik F1 nie obejmuje TN.

Chociaż wartości MCC są trudniejsze do zinterpretowania niż wynik F1, jest on uważany za lepszy wskaźnik, jak opisano w poniższym artykule: The advantages of the Matthews correlation coefficient (MCC) over F1 score and accuracy in binary classification evaluation autorstwa D. Chicco i G. Jurman, BMC Genomics. s. 281-305, 2012, https://bmcgenomics.biomedcentral.com/articles/10.1186/s1 864-019-6413-7.

Wszystkie te metryki punktacji są zaimplementowane w scikit-learn i mogą być importowane z modułu sklearn. metrics, jak pokazano w poniższym fragmencie:

In [ ]:
pre_val_lr = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_lr}')

rec_val_lr = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_lr}')

f1_val_lr = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_lr}')

mcc_val_lr = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_lr:.3f}')
Precision: 0.9393939393939394
Recall: 0.9243119266055045
F1: 0.9317919075144507
MCC: 0.842

Krzywa ROC¶

Krzywe ROC - ang.Receiver operating characteristic (ROC) są przydatnymi narzędziami do wyboru modeli do klasyfikacji na podstawie ich wydajności w odniesieniu do FPR i TPR, które są obliczane przez przesunięcie progu decyzyjnego klasyfikatora.

Przekątna wykresu ROC może być interpretowana jako losowe zgadywanie, a modele klasyfikacji, które spadają poniżej przekątnej, są uważane za gorsze niż losowe zgadywanie.

Idealny klasyfikator znalazłby się w lewym górnym rogu wykresu z TPR równym 1 i FPR równym 0. Na podstawie krzywej ROC możemy następnie obliczyć tak zwany obszar ROC pod krzywą (ROC AUC), aby scharakteryzować wydajność modelu klasyfikacji.

Wykonując poniższy przykład kodu, wykreślimy krzywą ROC klasyfikatora, który wykorzystuje tylko dwie cechy ze zbioru danych, aby przewidzieć, czy ryż jest klasy Cammeo, czy Osmancik. Chociaż zamierzamy użyć tego samego pipeline'u regresji logistycznej, który zdefiniowaliśmy wcześniej, tym razem używamy tylko dwóch cech. Ma to na celu uczynienie zadania klasyfikacji trudniejszym dla klasyfikatora, poprzez wstrzymanie przydatnych informacji zawartych w innych cechach, tak aby wynikowa krzywa ROC stała się bardziej interesująca wizualnie. Z podobnych powodów zmniejszamy również liczbę podziałów w walidatorze StratifiedKFold do trzech.

Kod wygląda następująco:

In [ ]:
pipe_lr = make_pipeline(
    StandardScaler(),
    PCA(n_components = 2),
    LogisticRegression(penalty = 'l2', random_state = 1,
                       solver = 'lbfgs', C = 100.0)
)
X_train2 = X_train[:, [1, 2]]
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))
fig = plt.figure(figsize=(7, 5))
mean_tpr = 0.0
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []
for i, (train, test) in enumerate(cv):
     probas = pipe_lr.fit(
          X_train2[train],
          y_train[train]).predict_proba(X_train2[test])
     fpr, tpr, thresholds = roc_curve(y_train[test],
                                      probas[:, 1],
                                      pos_label=1)
     mean_tpr += interp(mean_fpr, fpr, tpr)
     mean_tpr[0] = 0.0
     roc_auc = auc(fpr, tpr)
     plt.plot(fpr,
              tpr,
              label=f'ROC fold {i+1} (area = {roc_auc:.2f})')
plt.plot([0, 1],
         [0, 1],
         linestyle='--',
         color=(0.6, 0.6, 0.6),
         label='Random guessing (area=0.5)')
mean_tpr /= len(cv)
mean_tpr[-1] = 1.0
mean_auc = auc(mean_fpr, mean_tpr)
plt.plot(mean_fpr, mean_tpr, 'k--',
         label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)
plt.plot([0, 0, 1],
         [0, 1, 1],
         linestyle=':',
         color='black',
         label='Perfect performance (area=1.0)')
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.legend(loc='lower right')
plt.show()

W kodzie użyliśmy znanej już klasy StratifiedKFold z scikit-learn i obliczyliśmy wydajność ROC klasyfikatora LogisticRegression w naszym potoku pipe_lr przy użyciu funkcji roc_curve z modułu sklearn.metrics oddzielnie dla każdej iteracji. Ponadto interpolowaliśmy średnią krzywą ROC z trzech zagięć za pomocą funkcji interp, którą zaimportowaliśmy z NumPy i obliczyliśmy obszar pod krzywą za pomocą funkcji AUC.

Wynikowa krzywa ROC wskazuje, że występuje duża zbieżność między różnymi podziałami, a średnia wartość ROC AUC (0,98) mieści się pomiędzy idealnym wynikiem (1,0) a losowym zgadywaniem (0,5).

Zauważmy, że jeśli interesuje nas tylko wynik ROC AUC, możemy również bezpośrednio zaimportować funkcję roc_auc_score z podmodułu sklearn.metrics, która może być używana podobnie do innych funkcji punktacji (na przykład precision_score).

Raportowanie wydajności klasyfikatora jako ROC AUC może dostarczyć dalszych informacji na temat wydajności klasyfikatora w odniesieniu do niezrównoważonych próbek.

Jednakże, podczas gdy wynik dokładności może być interpretowany jako pojedynczy punkt odcięcia na krzywej ROC, A. P. Bradley wykazał, że wskaźniki ROC AUC i dokładności w większości zgadzają się ze sobą: The Use of the Area Under the ROC Curve in the Evaluation of Machine Learning Algorithms by A. P. Bradley, Pattern Recognition, 30(7): 1145-1159, 1997, https://reader.elsevier.com/reader/sd/pii/S0031320396001422

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

Tutatj przedstawiamy działanie krzywej uczenia się i krzywej walidacji do zdiagnozowania. Przyjrzymy się, czy algorytm ma problem z nadmiernym dopasowaniem (wysoka wariancja) lub też niedostatecznym dopasowaniem (wysokie odchylenie).

Jeśli model jest zby złożony dla danego zbioru danych treningowych - model ma wtedy tendencję do nadmiernego dopasowywania danych szkoleniowych i nie uogólnia się dobrze na niewidoczne dane.

Często pomocne może być zebranie większej liczby przykładów szkoleniowych, aby zmniejszyć stopień nadmiernego dopasowania.

Jednak w praktyce gromadzenie większej ilości danych może być często bardzo kosztowne lub po prostu niewykonalne.

Poprzez wykreślenie dokładności modelu treningowego i walidacyjnego jako funkcji wielkości zbioru danych treningowych, możemy łatwo wykryć, czy model cierpi z powodu dużej wariancji lub dużej stronniczości i czy zebranie większej ilości danych może pomóc w rozwiązaniu tego problemu.

Zobaczmy, jak możemy użyć funkcji krzywej uczenia się z scikit-learn do oceny modelu:

In [ ]:
pipe_lr = make_pipeline(StandardScaler(),
                        LogisticRegression(penalty='l2',
                                           max_iter=10000))
train_sizes, train_scores, test_scores = \
    learning_curve(estimator=pipe_lr,
                   X=X_train,
                   y=y_train,
                   train_sizes=np.linspace(
                       0.1, 1.0, 10),
                       cv=10,
                       n_jobs=1)
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.5, 1.05])
plt.show()

Przekazaliśmy max_iter = 10000 jako dodatkowy argument, podczas tworzenia instancji obiektu LogisticRegression (który domyślnie używa 1000 iteracji), aby uniknąć problemów ze zbieżnością dla mniejszych rozmiarów zbiorów danych lub ekstremalnych wartości parametrów regularyzacji.

Za pomocą parametru train_sizes w funkcji learning_curve możemy kontrolować bezwzględną lub względną liczbę przykładów treningowych, które są używane do generowania krzywych uczenia się.

Tutaj ustaliliśmy train_sizes = np.linspace(0.1,1.0,10), aby użyć 10 równomiernie rozmieszczonych względnych przedziałów dla rozmiarów zbioru danych treningowych.

Domyślnie funkcja learning_curve wykorzystuje k-krotną waldację krzyżową do obliczenia dokładności walidacji krzyżowej klasyfikatora, a my ustawiamy $k = 10$ za pomocą parametru cv dla 10-krotnej warstwowej walidacji krzyżowej.

Następnie po prostu obliczamy średnią dokładność na podstawie zwróconych zweryfikowanych krzyżowo wyników treningowych i testowych dla różnych rozmiarów zbioru danych treningowych, które wykreśliliśmy za pomocą funkcji wykresu Matplotlib.

Ponadto dodaliśmy odchylenie standardowe średniej dokładności do wykresu za pomocą funkcji fill_between aby wskazać wariancję oszacowania.

Jak widzimy na poprzednim wykresie krzywej uczenia, nasz model radzi sobie bardzo dobrze zarówno na zbiorze danych treningowych, jak i walidacyjnych.

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

Krzywe walidacyjne są przydatnym narzędziem do poprawy wydajności modelu poprzez rozwiązywanie problemów takich jak nadmierne lub niedostateczne dopasowanie.

Krzywe walidacji są powiązane z krzywymi uczenia się, ale zamiast wykreślać jako funkcji wielkości próby, zmieniamy wartości parametrów modelu, na przykład odwrotny parametr regularyzacji, C, w regresji logistycznej.

In [ ]:
param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]
train_scores, test_scores = validation_curve(
    estimator = pipe_lr,
    X = X_train,
    y = y_train,
    param_name = 'logisticregression__C',
    param_range = param_range,
    cv = 10)
train_mean = np.mean(train_scores , axis = 1)
train_std = np.std(train_scores, axis = 1)
test_mean = np.mean(test_scores, axis = 1)
test_std = np.std(test_scores, axis = 1)
plt.plot(param_range, train_mean, color = 'blue',
         marker = 'o', markersize = 5, 
         label = 'Training accuracy')
plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha = 0.15,
                 color = 'blue')
plt.plot(param_range, test_mean,
         color = 'green', linestyle = '--',
         marker = 's', markersize = 5,
         label = 'Validation accuracy')
plt.fill_between(param_range,
          test_mean + test_std,
          test_mean - test_std,
          alpha = 0.15, color = 'green')
plt.grid()
plt.xscale('log')
plt.legend(loc = 'lower right')
plt.xlabel('Parameter C')
plt.ylabel('Accuracy')
plt.ylim([0.5, 1.05])
plt.show()

Podobnie jak funkcja learning_curve, funkcja validation_curve domyślnie wykorzystuje warstwową k-krotną walidację krzyżową do oszacowania wydajności klasyfikatora.

Wewnątrz funkcji validation_curve określiliśmy parametr, który chcieliśmy ocenić. W tym przypadku jest to C, odwrotny parametr regularyzacji klasyfikatora LogisticRegression, który zapisaliśmy jako logisticregression__C.

Aby uzyskać dostęp do obiektu LogisticRegression wewnątrz potoku scikit-learn dla określonego zakresu wartości, który ustawiliśmy za pomocą parametru param_range.

Podobnie jak w przykładzie krzywej uczenia się w poprzedniej sekcji, wykreśliliśmy średnią dokładność treningu i walidacji krzyżowej oraz odpowiadające im odchylenia standardowe.

Widzimy, że dla dowolnego C, model dobrze dopasowywuje się do danych.

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

Wykorzystamy teraz technikę optymalizacji hiperparametrów (parametry algorytmu uczącego poddane osobnej optymalizacji) zwanej przeszukiwaniem siatki (Grid Search), która może dodatkowo pomóc poprawić wydajność modelu, poprzez znalezienie ich optymalnej kombinacji wartości.

Działanie Grid Searchu jest następujące: jest to paradygmat brutalnego wyszukiwania wyczerpującego, w którym określamy listę wartości dla różnych hiperparametrów, a komputer ocenia wydajność modelu dla każdej kombinacji, aby uzyskać optymalną kombinację wartości z tego zestawu.

In [ ]:
# Tworzenie potoku z użyciem LogisticRegression i StandardScaler
pipe_lr = make_pipeline(StandardScaler(), LogisticRegression(max_iter=10000, random_state=1))

# Definiowanie zakresu hiperparametrów
param_grid = {
    'logisticregression__C': [0.001, 0.01, 0.1, 1.0, 10.0, 100.0],  # Zakres parametru regularyzacji C
    'logisticregression__solver': ['liblinear', 'lbfgs', 'sag', 'saga']  # Wybór solvera
}

# Przeprowadzenie przeszukiwania siatki
gs_lr = GridSearchCV(estimator=pipe_lr,
                  param_grid=param_grid,
                  scoring='accuracy',
                  cv=10,
                  n_jobs=-1)
gs_lr = gs_lr.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(gs_lr.best_score_)
print(gs_lr.best_params_)
0.9314279551337359
{'logisticregression__C': 10.0, 'logisticregression__solver': 'sag'}

Zainicjowaliśmy obiekt GridSearchCV z modułu sklearn.model_selection do trenowania i dostrajania potoku LogisticRegression.

Ustawiliśmy parametr param_grid obiektu GridSearchCV na listę słowników, aby określić parametry, które chcemy dostroić.

GridSearchCV wykorzystuje k-krotną walidację krzyżową do porównywania modeli wytrenowanych z różnymi ustawieniami hiperparametrów. Poprzez ustawienie cv = 10, przeprowadzi 10-krotną walidację krzyżową i obliczy średnią dokładność (poprzez scoring='accuracy') w tych 10-krotnościach, aby ocenić wydajność modelu.

Ustawiliśmy n_jobs=-1, aby GridSearchCV mógł wykorzystać wszystkie nasze rdzenie przetwarzające w celu przyspieszenia wyszukiwania siatki poprzez równoległe dopasowywanie modeli do różnych podziałów.

Po wykorzystaniu danych treningowych do przeprowadzenia wyszukiwania siatki uzyskaliśmy wynik najlepiej działającego modelu za pośrednictwem atrybutu best_score_ i przyjrzeliśmy się jego parametrom, do których można uzyskać dostęp za pośrednictwem atrybutu best_params_.

W tym konkretnym przypadku solver sag z logisticregression__C = 10 uzyskał najlepszą dokładność k-kronej walidacji krzyżowej na poziomie $93\%$.

Na koniec wykorzystujemy niezależny zestaw danych testowych do oszacowania wydajności najlepiej wybranego modelu, który jest dostępny za pośrednictwem atrybutu best_estimator_ obiektu GridSearchCV

In [ ]:
clf_lr = gs_lr.best_estimator_
clf_lr.fit(X_train, y_train)
print(f'Test accuracy: {clf_lr.score(X_test, y_test):.3f}')
Test accuracy: 0.924

Co więcej, możemy użyć innej metryki punktacji niż dokładność w GridSearchCV poprzez parametr scoring parametr.

In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_lr,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)
0.9210531934579306
{'logisticregression__C': 100.0, 'logisticregression__solver': 'sag'}

Wybór algorytmu z zagnieżdżoną walidacją krzyżową¶

Korzystanie z k-krotnej walidacji krzyżowej w połączeniu z wyszukiwaniem siatki lub wyszukiwaniem randomizowanym jest użytecznym podejściem do dostrajania wydajności modelu uczenia maszynowego poprzez zmianę jego wartości hiperparametrów.

Jeśli jednak chcemy wybrać inny algoryt uczenia maszynowego, zalecanym podejściem jest zagnieżdżona walidacja krzyżowa.

W ciekawym badaniu na temat stronniczości w szacowaniu błędów, Sudhir Varma i Richard Simon doszli do wniosku, że prawdziwy błąd oszacowania jest prawie bezstronny w stosunku do zbioru danych testowych, gdy stosowana jest zagnieżdżona walidacja krzyżowa (Bias in Error Estimation When Using Cross-Validation for Model Selection by S. Varma and R. Simon, BMC Bioinformatics, 7(1): 91, 2006, https://bmcbioinformatics.biomedcentral.com/articl es/10.1186/1471-2105-7-91).

W zagnieżdżonej walidacji krzyżowej mamy zewnętrzną pętlę k-krotnej walidacji krzyżowej, aby podzielić dane na podziały treningowe i testowe, a wewnętrzna pętla służy do wyboru modelu przy użyciu k-krotnej walidacji krzyżowej na podziale treningowym. Po wyborze modelu, podział testowy jest następnie wykorzystywany do oceny wydajności modelu.

W scikit-learn możemy przeprowadzić zagnieżdżoną walidację krzyżową z wyszukiwaniem siatki w następujący sposób:

In [ ]:
nested_gs_lr = GridSearchCV(estimator = pipe_lr,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_lr = cross_val_score(nested_gs_lr, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_lr):.3f}'
      f' +/- {np.std(nested_scores_lr):.3f}')
CV accuracy: 0.930 +/- 0.010

Zwrócona średnia dokładność walidacji krzyżowej daje nam dobre oszacowanie tego, czego możemy się spodziewać, jeśli dostroimy hiperparametry modelu i użyjemy go na niewidocznych danych.

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

Pakiet scikit-learn implementuje klasę RandomizedSearchCV, która jest analogiczna do GridSearchCV. Główną różnicą jest to, że możemy określić dystrybucje jako część naszej siatki parametrów i określić całkowitą liczbę konfiguracji hiperparametrów do oceny.

Na przykład, rozważmy zakres hiperparametrów, którego użyliśmy dla kilku hiperparametrów podczas dostrajania Logistic Regression w przykładzie wcześniej wspomnianego wyszukiwania siatki.

In [ ]:
# Tworzenie potoku z użyciem StandardScaler i LogisticRegression
pipe_lr = make_pipeline(StandardScaler(), LogisticRegression(max_iter=10000, random_state=1))

# Definiowanie zakresu hiperparametrów
param_dist = {
    'logisticregression__C': [0.001, 0.01, 0.1, 1.0, 10.0, 100.0],  # Zakres parametru regularyzacji C
    'logisticregression__solver': ['liblinear', 'lbfgs', 'sag', 'saga']  # Wybór solvera
}

# Przeprowadzenie przeszukiwania losowego
rs_lr = RandomizedSearchCV(estimator=pipe_lr,
                        param_distributions=param_dist,
                        scoring='accuracy',
                        n_iter=20,
                        cv=10,
                        refit=True,
                        random_state=1,
                        n_jobs=-1)
rs_lr.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(rs_lr.best_score_)
print(rs_lr.best_params_)
0.9314279551337359
{'logisticregression__solver': 'sag', 'logisticregression__C': 10.0}

SVM¶

In [ ]:
# Tworzenie obiektu SVM
svm = SVC(kernel = 'linear', C = 10.0, random_state = 1)

# Dopasowanie modelu SVM do danych z PCA
svm.fit(X_train_pca, y_train)

# Wizualizacja wyników na zbiorze treningowym po PCA
plot_decision_regions(X_train_pca, y_train, classifier=svm)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()
In [ ]:
# Wizualizacja wyników na zbiorze testowym po PCA
plot_decision_regions(X_test_pca, y_test, classifier=svm)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu SVM¶

Łączenie transformatorów i estymatorów w potoku¶

In [ ]:
pipe_svc = make_pipeline(StandardScaler(),
                        SVC(kernel = 'linear',
                            C = 10.0,
                            random_state=1))
pipe_svc.fit(X_train, y_train)
y_pred = pipe_svc.predict(X_test)
test_acc_svc = pipe_svc.score(X_test, y_test)
print(f'Test accuracy: {test_acc_svc: .3f}')
Test accuracy:  0.920

Macierz pomyłek¶

In [ ]:
pipe_svc.fit(X_train, y_train)
y_pred = pipe_svc.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[297  29]
 [ 32 404]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_svc = cross_val_score(estimator = pipe_svc,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_svc}')
print(f'CV accuracy scores: {np.mean(scores_svc):.3f}'
      f' +/- {np.std(scores_svc):.3f}')
CV accuracy scores: [0.93442623 0.93770492 0.91803279 0.93770492 0.93442623 0.96065574
 0.93442623 0.9147541  0.91776316 0.92434211]
CV accuracy scores: 0.931 +/- 0.013

Optymalizacja dokładności (precision), wyniku wycofania (recall score), wyniku f1 (F1 score), oraz współczynnika mathhews dla modelu klasyfikacji¶

In [ ]:
pre_val_svc = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_svc}')

rec_val_svc = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_svc}')

f1_val_svc = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_svc}')

mcc_val_svc = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_svc:.3f}')
Precision: 0.9330254041570438
Recall: 0.926605504587156
F1: 0.9298043728423475
MCC: 0.837

Krzywa ROC¶

In [ ]:
# Tworzenie potoku z użyciem SVC i StandardScaler
pipe_svc = make_pipeline(StandardScaler(), SVC(kernel = 'linear', C = 10.0, probability=True, random_state=1))

# Podział danych do walidacji krzyżowej
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))

# Inicjalizacja wykresu
fig = plt.figure(figsize=(7, 5))
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []

# Dla każdego podziału walidacji krzyżowej
for i, (train, test) in enumerate(cv):
    # Dopasowanie modelu do danych treningowych
    probas = pipe_svc.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    
    # Obliczenie współczynników True Positive Rate (TPR) oraz False Positive Rate (FPR)
    fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1)
    
    # Interpolacja wyników na średnią krzywą ROC
    mean_tpr = np.interp(mean_fpr, fpr, tpr)
    mean_tpr[0] = 0.0
    all_tpr.append(mean_tpr)
    
    # Obliczenie pola pod krzywą ROC (AUC)
    roc_auc = auc(fpr, tpr)
    
    # Wyświetlenie krzywej ROC dla każdego podziału
    plt.plot(fpr, tpr, label=f'ROC fold {i+1} (area = {roc_auc:.2f})')

# Obliczenie średniego True Positive Rate
mean_tpr = np.mean(all_tpr, axis=0)
mean_tpr[-1] = 1.0

# Obliczenie średniego pola pod krzywą ROC (AUC)
mean_auc = auc(mean_fpr, mean_tpr)

# Wyświetlenie średniej krzywej ROC
plt.plot(mean_fpr, mean_tpr, 'k--', label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)

# Wyświetlenie linii referencyjnych
plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='Random guessing (area=0.5)')
plt.plot([0, 0, 1], [0, 1, 1], linestyle=':', color='black', label='Perfect performance (area=1.0)')

# Ustawienia osi i legendy
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

In [ ]:
# Tworzenie potoku z użyciem StandardScaler i SVC
pipe_svc = make_pipeline(StandardScaler(), SVC(kernel='rbf', random_state=1))

# Wygenerowanie krzywej uczenia
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_svc,
                                                        X=X_train,
                                                        y=y_train,
                                                        train_sizes=np.linspace(0.1, 1.0, 10),
                                                        cv=10,
                                                        n_jobs=1)

# Obliczenie średnich i odchyleń standardowych dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej uczenia
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.0])
plt.show()

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

In [ ]:
pipe_svc = make_pipeline(StandardScaler(), SVC(kernel='rbf', random_state=1))

param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]

# Wygeneruj krzywą walidacji dla parametru C w SVC
train_scores, test_scores = validation_curve(estimator=pipe_svc,
                                             X=X_train,
                                             y=y_train,
                                             param_name='svc__C',
                                             param_range=param_range,
                                             cv=10)

train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreśl krzywą walidacji
plt.plot(param_range, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')
plt.plot(param_range, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(param_range,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xscale('log')
plt.legend(loc='lower right')
plt.xlabel('Parameter C')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.0])
plt.title('Validation Curve for SVM')
plt.show()

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

In [ ]:
pipe_svc = make_pipeline(StandardScaler(),
                         SVC(random_state = 1))
param_range = [0.001, 0.01, 0.1,
               1.0, 10.0, 100.0]
param_grid = [{'svc__C': param_range,
               'svc__kernel': ['linear']},
               {'svc__C': param_range,
                'svc__gamma': param_range,
                'svc__kernel':['rbf']}]
gs_svc = GridSearchCV(estimator = pipe_svc,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 10,
                  refit = True,
                  n_jobs = -1)
gs_svc = gs_svc.fit(X_train, y_train)
print(gs_svc.best_score_)
print(gs_svc.best_params_)
0.9314236410698877
{'svc__C': 10.0, 'svc__kernel': 'linear'}
In [ ]:
clf_svc = gs_svc.best_estimator_
clf_svc.fit(X_train, y_train)
print(f'Test accuracy: {clf_svc.score(X_test, y_test):.3f}')
Test accuracy: 0.920
In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_svc,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)
0.9204763085497031
{'svc__C': 10.0, 'svc__kernel': 'linear'}

Zagnieżdżona walidacja krzyżowa z wykorzystaniem GridSearchCV¶

In [ ]:
nested_gs_svc = GridSearchCV(estimator = pipe_svc,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_svc = cross_val_score(nested_gs_svc, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_svc):.3f}'
      f' +/- {np.std(nested_scores_svc):.3f}')
CV accuracy: 0.930 +/- 0.010

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

In [ ]:
pipe_svc = make_pipeline(StandardScaler(),
                         SVC(random_state = 1))
param_dist = [{'svc__C': param_range,
               'svc__kernel': ['linear']},
               {'svc__C': param_range,
                'svc__gamma': param_range,
                'svc__kernel':['rbf']}]

rs_svc = RandomizedSearchCV(estimator=pipe_svc,
                        param_distributions=param_dist,
                        scoring='accuracy',
                        refit=True,
                        n_iter=20,
                        cv=10,
                        random_state=1,
                        n_jobs=-1)

rs_svc = rs_svc.fit(X_train, y_train)
print(rs_svc.best_score_)
print(rs_svc.best_params_)
0.9314236410698877
{'svc__kernel': 'linear', 'svc__C': 10.0}

Decimal Decision Tree¶

In [ ]:
# Tworzenie obiektu klasyfikatora drzewa decyzyjnego
tree = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=1)

# Dopasowanie modelu do danych PCA
tree.fit(X_train_pca, y_train)

# Wizualizacja wyników na zbiorze treningowym po PCA
plot_decision_regions(X_train_pca, y_train, classifier=tree)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()
In [ ]:
# Wizualizacja wyników na zbiorze testowym po PCA
plot_decision_regions(X_test_pca, y_test, classifier=tree)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu Drzewa Decyzyjnego¶

Łączenie transformatorów i estymatorów w potoku¶

In [ ]:
# Utwórz potok zawierający StandardScaler i DecisionTreeClassifier
pipe_dt = make_pipeline(StandardScaler(), DecisionTreeClassifier(random_state=1))

# Trenuj model na danych treningowych
pipe_dt.fit(X_train, y_train)

# Dokonaj predykcji na danych testowych
y_pred_dt = pipe_dt.predict(X_test)

# Oblicz dokładność modelu na danych testowych
test_acc_dt = pipe_dt.score(X_test, y_test)
print(f'Test accuracy (DecisionTreeClassifier): {test_acc_dt:.3f}')
Test accuracy (DecisionTreeClassifier): 0.888

Macierz pomyłek¶

In [ ]:
pipe_dt.fit(X_train, y_train)
y_pred = pipe_dt.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[288  38]
 [ 47 389]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_dt = cross_val_score(estimator = pipe_dt,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_dt}')
print(f'CV accuracy scores: {np.mean(scores_dt):.3f}'
      f' +/- {np.std(scores_dt):.3f}')
CV accuracy scores: [0.89836066 0.90163934 0.8852459  0.90491803 0.89836066 0.89180328
 0.89508197 0.88196721 0.875      0.85526316]
CV accuracy scores: 0.889 +/- 0.014

Optymalizacja dokładności (precision), wyniku wycofania (recall score), wyniku f1 (F1 score), oraz współczynnika mathhews dla modelu klasyfikacji¶

In [ ]:
pre_val_dt = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_dt}')

rec_val_dt = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_dt}')

f1_val_dt = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_dt}')

mcc_val_dt = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_dt:.3f}')
Precision: 0.9110070257611241
Recall: 0.8922018348623854
F1: 0.9015063731170336
MCC: 0.773

Krzywa ROC¶

In [ ]:
# Podział danych do walidacji krzyżowej
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))

# Inicjalizacja wykresu
fig = plt.figure(figsize=(7, 5))
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []

# Dla każdego podziału walidacji krzyżowej
for i, (train, test) in enumerate(cv):
    # Dopasowanie modelu do danych treningowych
    probas = pipe_dt.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    
    # Obliczenie współczynników True Positive Rate (TPR) oraz False Positive Rate (FPR)
    fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1)
    
    # Interpolacja wyników na średnią krzywą ROC
    mean_tpr = np.interp(mean_fpr, fpr, tpr)
    mean_tpr[0] = 0.0
    all_tpr.append(mean_tpr)
    
    # Obliczenie pola pod krzywą ROC (AUC)
    roc_auc = auc(fpr, tpr)
    
    # Wyświetlenie krzywej ROC dla każdego podziału
    plt.plot(fpr, tpr, label=f'ROC fold {i+1} (area = {roc_auc:.2f})')

# Obliczenie średniego True Positive Rate
mean_tpr = np.mean(all_tpr, axis=0)
mean_tpr[-1] = 1.0

# Obliczenie średniego pola pod krzywą ROC (AUC)
mean_auc = auc(mean_fpr, mean_tpr)

# Wyświetlenie średniej krzywej ROC
plt.plot(mean_fpr, mean_tpr, 'k--', label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)

# Wyświetlenie linii referencyjnych
plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='Random guessing (area=0.5)')
plt.plot([0, 0, 1], [0, 1, 1], linestyle=':', color='black', label='Perfect performance (area=1.0)')

# Ustawienia osi i legendy
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

In [ ]:
# Wygenerowanie krzywej uczenia
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_dt,
                                                        X=X_train,
                                                        y=y_train,
                                                        train_sizes=np.linspace(0.1, 1.0, 10),
                                                        cv=10,
                                                        n_jobs=1)

# Obliczenie średnich i odchyleń standardowych dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej uczenia
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.05])
plt.show()

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

In [ ]:
pipe_dt = DecisionTreeClassifier(random_state = 1)

# Zdefiniuj zakres parametru max_depth do testowania
param_range = np.arange(1, 21)

# Wygeneruj krzywą walidacji dla parametru max_depth w DecisionTreeClassifier
train_scores, test_scores = validation_curve(estimator=pipe_dt,
                                             X=X_train,
                                             y=y_train,
                                             param_name='max_depth',
                                             param_range=param_range,
                                             cv=10)

# Oblicz średnie dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreśl krzywą walidacji
plt.plot(param_range, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')
plt.plot(param_range, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(param_range,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.legend(loc='lower right')
plt.xlabel('Parameter max_depth')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.05])
plt.title('Validation Curve for Decision Tree Classifier')
plt.show()

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

In [ ]:
pipe_dt = make_pipeline(StandardScaler(), DecisionTreeClassifier(random_state = 1))

# Definicja siatki parametrów do przeszukania
param_grid = {
    'decisiontreeclassifier__max_depth': [None, 1, 2, 3, 4, 5], # Przykładowe wartości dla parametru max_depth
    'decisiontreeclassifier__min_samples_split': [2, 5, 10], # Przykładowe wartości dla parametru min_samples_split
    'decisiontreeclassifier__min_samples_leaf': [1, 2, 3, 4, 5], # Przykładowe wartości dla parametru min_samples_leaf
    'decisiontreeclassifier__criterion': ['gini', 'entropy'] # Wybór kryterium
}

# Utworzenie obiektu GridSearchCV
gs_dt = GridSearchCV(estimator=pipe_dt,
                  param_grid=param_grid,
                  scoring='accuracy',  # Wybór metryki oceny
                  cv=5,  # Liczba podziałów walidacji krzyżowej
                  n_jobs=-1)  # Wykorzystanie wszystkich rdzeni procesora

# Uruchomienie procesu przeszukiwania siatki hiperparametrów
gs_dt.fit(X_train, y_train)

# Wyświetlenie najlepszych parametrów i wyników
print(gs_dt.best_score_)
print(gs_dt.best_params_)
0.9245368650569329
{'decisiontreeclassifier__criterion': 'gini', 'decisiontreeclassifier__max_depth': 1, 'decisiontreeclassifier__min_samples_leaf': 1, 'decisiontreeclassifier__min_samples_split': 2}
In [ ]:
clf_dt = gs_dt.best_estimator_
clf_dt.fit(X_train, y_train)
print(f'Test accuracy: {clf_dt.score(X_test, y_test):.3f}')
Test accuracy: 0.927
In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_dt,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)
0.9142075721158038
{'decisiontreeclassifier__criterion': 'gini', 'decisiontreeclassifier__max_depth': 1, 'decisiontreeclassifier__min_samples_leaf': 1, 'decisiontreeclassifier__min_samples_split': 2}

Zagnieżdżona walidacja krzyżowa z wykorzystaniem GridSearchCV¶

In [ ]:
nested_gs_dt = GridSearchCV(estimator = pipe_dt,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_dt = cross_val_score(nested_gs_dt, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_dt):.3f}'
      f' +/- {np.std(nested_scores_dt):.3f}')
CV accuracy: 0.925 +/- 0.006

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

In [ ]:
# Definicja przestrzeni parametrów do przeszukania
param_dist = {
    'decisiontreeclassifier__max_depth': randint(1, 20), # Przykładowe wartości dla parametru max_depth
    'decisiontreeclassifier__min_samples_split': randint(2, 50), # Przykładowe wartości dla parametru min_samples_split
    'decisiontreeclassifier__min_samples_leaf': randint(1, 20), # Przykładowe wartości dla parametru min_samples_leaf
    'decisiontreeclassifier__criterion': ['gini', 'entropy'] # Wybór kryterium
}

# Inicjalizacja RandomizedSearchCV
rs_dt = RandomizedSearchCV(
    estimator=pipe_dt,
    param_distributions=param_dist,
    scoring='accuracy',
    cv=5,
    n_iter=20,
    n_jobs=-1,
    random_state=42
)

# Dopasowanie modelu
rs_dt.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(rs_dt.best_score_)
print(rs_dt.best_params_)
0.9255226251043096
{'decisiontreeclassifier__criterion': 'entropy', 'decisiontreeclassifier__max_depth': 4, 'decisiontreeclassifier__min_samples_leaf': 14, 'decisiontreeclassifier__min_samples_split': 32}

KNN¶

In [ ]:
knn = KNeighborsClassifier(n_neighbors=5, p=2,
                            metric='minkowski')
knn.fit(X_train_pca, y_train)
plot_decision_regions(X_train_pca, y_train,
                      classifier=knn)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
In [ ]:
plot_decision_regions(X_test_pca, y_test, classifier=knn)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu KNN¶

Łączenie transformatorów i estymatorów w potoku¶

In [ ]:
pipe_knn = make_pipeline(StandardScaler(),
                        KNeighborsClassifier(n_neighbors=5, 
                                             p=2,
                                             metric='minkowski'))
pipe_knn.fit(X_train, y_train)
y_pred = pipe_knn.predict(X_test)
test_acc_knn = pipe_knn.score(X_test, y_test)
print(f'Test accuracy: {test_acc_knn: .3f}')
Test accuracy:  0.919

Macierz pomyłek¶

In [ ]:
pipe_knn.fit(X_train, y_train)
y_pred = pipe_knn.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[297  29]
 [ 33 403]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_knn = cross_val_score(estimator = pipe_knn,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_knn}')
print(f'CV accuracy scores: {np.mean(scores_knn):.3f}'
      f' +/- {np.std(scores_knn):.3f}')
CV accuracy scores: [0.92459016 0.90491803 0.90819672 0.94098361 0.91803279 0.94098361
 0.92786885 0.90491803 0.92434211 0.90789474]
CV accuracy scores: 0.920 +/- 0.013

Optymalizacja dokładności (precision), wyniku wycofania (recall score), wyniku f1 (F1 score), oraz współczynnika mathhews dla modelu klasyfikacji¶

In [ ]:
pre_val_knn = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_knn}')

rec_val_knn = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_knn}')

f1_val_knn = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_knn}')

mcc_val_knn = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_knn:.3f}')
Precision: 0.9328703703703703
Recall: 0.9243119266055045
F1: 0.9285714285714285
MCC: 0.834

Krzywa ROC¶

In [ ]:
# Tworzenie potoku z użyciem KNeighborsClassifier i StandardScaler
pipe_knn = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5, 
                                                               p=2,
                                                               metric='minkowski'))

# Podział danych do walidacji krzyżowej
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))

# Inicjalizacja wykresu
fig = plt.figure(figsize=(7, 5))
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []

# Dla każdego podziału walidacji krzyżowej
for i, (train, test) in enumerate(cv):
    # Dopasowanie modelu do danych treningowych
    probas = pipe_knn.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    
    # Obliczenie współczynników True Positive Rate (TPR) oraz False Positive Rate (FPR)
    fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1)
    
    # Interpolacja wyników na średnią krzywą ROC
    mean_tpr = np.interp(mean_fpr, fpr, tpr)
    mean_tpr[0] = 0.0
    all_tpr.append(mean_tpr)
    
    # Obliczenie pola pod krzywą ROC (AUC)
    roc_auc = auc(fpr, tpr)
    
    # Wyświetlenie krzywej ROC dla każdego podziału
    plt.plot(fpr, tpr, label=f'ROC fold {i+1} (area = {roc_auc:.2f})')

# Obliczenie średniego True Positive Rate
mean_tpr = np.mean(all_tpr, axis=0)
mean_tpr[-1] = 1.0

# Obliczenie średniego pola pod krzywą ROC (AUC)
mean_auc = auc(mean_fpr, mean_tpr)

# Wyświetlenie średniej krzywej ROC
plt.plot(mean_fpr, mean_tpr, 'k--', label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)

# Wyświetlenie linii referencyjnych
plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='Random guessing (area=0.5)')
plt.plot([0, 0, 1], [0, 1, 1], linestyle=':', color='black', label='Perfect performance (area=1.0)')

# Ustawienia osi i legendy
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

In [ ]:
# Wygenerowanie krzywej uczenia
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_knn,
                                                        X=X_train,
                                                        y=y_train,
                                                        train_sizes=np.linspace(0.1, 1.0, 10),
                                                        cv=10,
                                                        n_jobs=1)

# Obliczenie średnich i odchyleń standardowych dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej uczenia
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.05])
plt.show()

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

In [ ]:
pipe_knn = KNeighborsClassifier()

# Zdefiniuj zakres parametru n_neighbors do testowania
param_range = range(1, 11)

# Wygeneruj krzywą walidacji dla parametru n_neighbors w KNeighborsClassifier
train_scores, test_scores = validation_curve(estimator=pipe_knn,
                                             X=X_train,
                                             y=y_train,
                                             param_name='n_neighbors',
                                             param_range=param_range,
                                             cv=10)

# Oblicz średnie dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreśl krzywą walidacji
plt.plot(param_range, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')
plt.plot(param_range, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(param_range,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.legend(loc='lower right')
plt.xlabel('Parameter n_neighbors')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.05])
plt.title('Validation Curve for KNN Classifier')
plt.show()

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

In [ ]:
# Utworzenie potoku zawierającego StandardScaler i KNeighborsClassifier
pipe_knn = make_pipeline(StandardScaler(), KNeighborsClassifier())

# Definicja siatki parametrów do przeszukania
param_grid = {
    'kneighborsclassifier__n_neighbors': [3, 5, 7, 9, 11],  # Przykładowe wartości dla parametru n_neighbors
    'kneighborsclassifier__metric': ['euclidean', 'manhattan', 'minkowski'],  # Przykładowe wartości dla parametru metric
}

# Utworzenie obiektu GridSearchCV
gs_knn = GridSearchCV(estimator=pipe_knn,
                  param_grid=param_grid,
                  scoring='accuracy',  # Wybór metryki oceny
                  cv=5,  # Liczba podziałów walidacji krzyżowej
                  n_jobs=-1)  # Wykorzystanie wszystkich rdzeni procesora

# Uruchomienie procesu przeszukiwania siatki hiperparametrów
gs_knn.fit(X_train, y_train)

# Wyświetlenie najlepszych parametrów i wyników
print(gs_knn.best_score_)
print(gs_knn.best_params_)
0.9258494172117688
{'kneighborsclassifier__metric': 'euclidean', 'kneighborsclassifier__n_neighbors': 11}
In [ ]:
clf_knn = gs_knn.best_estimator_
clf_knn.fit(X_train, y_train)
print(f'Test accuracy: {clf_knn.score(X_test, y_test):.3f}')
Test accuracy: 0.920
In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_knn,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)
0.9160892994611238
{'kneighborsclassifier__metric': 'euclidean', 'kneighborsclassifier__n_neighbors': 11}

Zagnieżdżona walidacja krzyżowa z wykorzystaniem GridSearchCV¶

In [ ]:
nested_gs_knn = GridSearchCV(estimator = pipe_knn,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_knn = cross_val_score(nested_gs_knn, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_knn):.3f}'
      f' +/- {np.std(nested_scores_knn):.3f}')
CV accuracy: 0.924 +/- 0.007

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

In [ ]:
# Definicja przestrzeni parametrów do przeszukania
param_dist = {
    'kneighborsclassifier__n_neighbors': randint(1, 20),  # Przykładowe wartości dla parametru n_neighbors
    'kneighborsclassifier__metric': ['euclidean', 'manhattan', 'minkowski'],  # Przykładowe wartości dla parametru metric
}

# Inicjalizacja RandomizedSearchCV
rs_knn = RandomizedSearchCV(
    estimator=pipe_knn,
    param_distributions=param_dist,
    scoring='accuracy',
    cv=5,
    n_iter=20,
    n_jobs=-1,
    random_state=42
)

# Dopasowanie modelu
rs_knn.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(rs_knn.best_score_)
print(rs_knn.best_params_)
0.9258494172117688
{'kneighborsclassifier__metric': 'minkowski', 'kneighborsclassifier__n_neighbors': 11}

Lasy losowe¶

In [ ]:
# Inicjalizacja lasu losowego
forest = RandomForestClassifier(n_estimators=100, random_state=1)
forest.fit(X_train_pca, y_train)

# Wygenerowanie wizualizacji granic decyzyjnych
plot_decision_regions(X_train_pca, y_train, classifier=forest)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
In [ ]:
plot_decision_regions(X_test_pca, y_test, classifier=forest)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu lasów losowych¶

Łączenie transformatorów i estymatorów w potoku¶

In [ ]:
pipe_forest = make_pipeline(StandardScaler(),
                        RandomForestClassifier(n_estimators=100,
                                                random_state=1))
pipe_forest.fit(X_train, y_train)
y_pred = pipe_forest.predict(X_test)
test_acc_forest = pipe_forest.score(X_test, y_test)
print(f'Test accuracy: {test_acc_forest: .3f}')
Test accuracy:  0.925

Macierz pomyłek¶

In [ ]:
pipe_forest.fit(X_train, y_train)
y_pred = pipe_forest.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[298  28]
 [ 29 407]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_forest = cross_val_score(estimator = pipe_forest,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_forest}')
print(f'CV accuracy scores: {np.mean(scores_forest):.3f}'
      f' +/- {np.std(scores_forest):.3f}')
CV accuracy scores: [0.93114754 0.92786885 0.90491803 0.92459016 0.92459016 0.95409836
 0.90163934 0.91803279 0.91447368 0.90789474]
CV accuracy scores: 0.921 +/- 0.015

Optymalizacja dokładności (precision), wyniku wycofania (recall score), wyniku f1 (F1 score), oraz współczynnika mathhews dla modelu klasyfikacji¶

In [ ]:
pre_val_forest = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_forest}')

rec_val_forest = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_forest}')

f1_val_forest = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_forest}')

mcc_val_forest = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_forest:.3f}')
Precision: 0.9356321839080459
Recall: 0.9334862385321101
F1: 0.9345579793340987
MCC: 0.847

Krzywa ROC¶

In [ ]:
# Podział danych do walidacji krzyżowej
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))

# Inicjalizacja wykresu
fig = plt.figure(figsize=(7, 5))
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []

# Dla każdego podziału walidacji krzyżowej
for i, (train, test) in enumerate(cv):
    # Dopasowanie modelu do danych treningowych
    probas = pipe_forest.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    
    # Obliczenie współczynników True Positive Rate (TPR) oraz False Positive Rate (FPR)
    fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1)
    
    # Interpolacja wyników na średnią krzywą ROC
    mean_tpr = np.interp(mean_fpr, fpr, tpr)
    mean_tpr[0] = 0.0
    all_tpr.append(mean_tpr)
    
    # Obliczenie pola pod krzywą ROC (AUC)
    roc_auc = auc(fpr, tpr)
    
    # Wyświetlenie krzywej ROC dla każdego podziału
    plt.plot(fpr, tpr, label=f'ROC fold {i+1} (area = {roc_auc:.2f})')

# Obliczenie średniego True Positive Rate
mean_tpr = np.mean(all_tpr, axis=0)
mean_tpr[-1] = 1.0

# Obliczenie średniego pola pod krzywą ROC (AUC)
mean_auc = auc(mean_fpr, mean_tpr)

# Wyświetlenie średniej krzywej ROC
plt.plot(mean_fpr, mean_tpr, 'k--', label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)

# Wyświetlenie linii referencyjnych
plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='Random guessing (area=0.5)')
plt.plot([0, 0, 1], [0, 1, 1], linestyle=':', color='black', label='Perfect performance (area=1.0)')

# Ustawienia osi i legendy
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

In [ ]:
# Wygenerowanie krzywej uczenia
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_forest,
                                                        X=X_train,
                                                        y=y_train,
                                                        train_sizes=np.linspace(0.1, 1.0, 10),
                                                        cv=10,
                                                        n_jobs=1)

# Obliczenie średnich i odchyleń standardowych dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej uczenia
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.05])
plt.show()

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

In [ ]:
pipe_forest = RandomForestClassifier(random_state=1)

# Zdefiniowanie zakresu parametru n_estimators do testowania
param_range = [10, 50, 100, 200]

# Wygenerowanie krzywej walidacji dla parametru n_estimators w RandomForestClassifier
train_scores, test_scores = validation_curve(estimator=pipe_forest,
                                             X=X_train,
                                             y=y_train,
                                             param_name='n_estimators',
                                             param_range=param_range,
                                             cv=10)

# Obliczenie średnich dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej walidacji
plt.plot(param_range, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')
plt.plot(param_range, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(param_range,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.legend(loc='lower right')
plt.xlabel('Parameter n_estimators')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.05])
plt.title('Validation Curve for RandomForestClassifier')
plt.show()

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

In [ ]:
# Utworzenie potoku zawierającego StandardScaler i RandomForestClassifier
pipe_forest = make_pipeline(StandardScaler(), RandomForestClassifier(random_state=1))

# Definicja siatki parametrów do przeszukania
param_grid = {
    'randomforestclassifier__n_estimators': [50, 100, 150],  # Liczba estymatorów
    'randomforestclassifier__max_depth': [None, 5, 10, 15],  # Maksymalna głębokość drzewa
    'randomforestclassifier__bootstrap': [True, False]  # Czy stosować bootstrap podczas budowy drzew
}


# Utworzenie obiektu GridSearchCV
gs_forest = GridSearchCV(estimator=pipe_forest,
                  param_grid=param_grid,
                  scoring='accuracy',  # Wybór metryki oceny
                  cv=5,  # Liczba podziałów walidacji krzyżowej
                  n_jobs=-1)  # Wykorzystanie wszystkich rdzeni procesora

# Uruchomienie procesu przeszukiwania siatki hiperparametrów
gs_forest.fit(X_train, y_train)

# Wyświetlenie najlepszych parametrów i wyników
print(gs_forest.best_score_)
print(gs_forest.best_params_)
0.927159815876605
{'randomforestclassifier__bootstrap': False, 'randomforestclassifier__max_depth': 5, 'randomforestclassifier__n_estimators': 50}
In [ ]:
clf_forest = gs_forest.best_estimator_
clf_forest.fit(X_train, y_train)
print(f'Test accuracy: {clf_forest.score(X_test, y_test):.3f}')
Test accuracy: 0.928
In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_forest,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)
0.9130744027489555
{'randomforestclassifier__bootstrap': True, 'randomforestclassifier__max_depth': 5, 'randomforestclassifier__n_estimators': 100}

Zagnieżdżona walidacja krzyżowa z wykorzystaniem GridSearchCV¶

In [ ]:
nested_gs_forest = GridSearchCV(estimator = pipe_forest,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_forest = cross_val_score(nested_gs_forest, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_forest):.3f}'
      f' +/- {np.std(nested_scores_forest):.3f}')
CV accuracy: 0.924 +/- 0.010

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

In [ ]:
# Definicja przestrzeni parametrów do przeszukania
param_dist = {
    'randomforestclassifier__n_estimators': randint(100, 1000),  # Liczba estymatorów
    'randomforestclassifier__max_depth': [None, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],  # Maksymalna głębokość drzewa
    'randomforestclassifier__min_samples_split': randint(2, 20),  # Minimalna liczba próbek wymagana do podziału węzła
    'randomforestclassifier__min_samples_leaf': randint(1, 20),  # Minimalna liczba próbek wymagana w liściu węzła
    'randomforestclassifier__bootstrap': [True, False]  # Czy stosować bootstrap podczas budowy drzew
}

# Inicjalizacja RandomizedSearchCV
rs_forest = RandomizedSearchCV(
    estimator=pipe_forest,
    param_distributions=param_dist,
    scoring='accuracy',
    cv=5,
    n_iter=20,
    n_jobs=-1,
    random_state=42
)

# Dopasowanie modelu
rs_forest.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(rs_forest.best_score_)
print(rs_forest.best_params_)
0.9284718296589409
{'randomforestclassifier__bootstrap': True, 'randomforestclassifier__max_depth': 30, 'randomforestclassifier__min_samples_leaf': 15, 'randomforestclassifier__min_samples_split': 12, 'randomforestclassifier__n_estimators': 171}

Sieci neuronowe¶

In [ ]:
# Inicjalizacja klasyfikatora MLP
mlp = MLPClassifier(hidden_layer_sizes=(100, 100), max_iter=1000, random_state=1)

# Dopasowanie klasyfikatora do danych
mlp.fit(X_train_pca, y_train)

# Wizualizacja granic decyzyjnych
plot_decision_regions(X_train_pca, y_train, classifier=mlp)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.title('Decision Boundary for MLP')
plt.show()
In [ ]:
plot_decision_regions(X_test_pca, y_test, classifier=mlp)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu sieci neurnonowych¶

Łączenie transformatorów i estymatorów w potoku¶

In [ ]:
pipe_mlp = make_pipeline(StandardScaler(),
                        MLPClassifier(hidden_layer_sizes=(100, 100),
                                    max_iter=1000,
                                    random_state=1))
pipe_mlp.fit(X_train, y_train)
y_pred = pipe_mlp.predict(X_test)
test_acc_mlp = pipe_mlp.score(X_test, y_test)
print(f'Test accuracy: {test_acc_mlp: .3f}')
Test accuracy:  0.925

Macierz pomyłek¶

In [ ]:
pipe_mlp.fit(X_train, y_train)
y_pred = pipe_mlp.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[300  26]
 [ 31 405]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_mlp = cross_val_score(estimator = pipe_mlp,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_mlp}')
print(f'CV accuracy scores: {np.mean(scores_mlp):.3f}'
      f' +/- {np.std(scores_mlp):.3f}')
CV accuracy scores: [0.93770492 0.93114754 0.91803279 0.94754098 0.92459016 0.95737705
 0.92786885 0.90491803 0.91776316 0.91447368]
CV accuracy scores: 0.928 +/- 0.015

Optymalizacja dokładności (precision), wyniku wycofania (recall score), wyniku f1 (F1 score), oraz współczynnika mathhews dla modelu klasyfikacji¶

In [ ]:
pre_val_mlp = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_mlp}')

rec_val_mlp = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_mlp}')

f1_val_mlp = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_mlp}')

mcc_val_mlp = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_mlp:.3f}')
Precision: 0.9396751740139211
Recall: 0.9288990825688074
F1: 0.9342560553633218
MCC: 0.848

Krzywa ROC¶

In [ ]:
# Podział danych do walidacji krzyżowej
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))

# Inicjalizacja wykresu
fig = plt.figure(figsize=(7, 5))
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []

# Dla każdego podziału walidacji krzyżowej
for i, (train, test) in enumerate(cv):
    # Dopasowanie modelu do danych treningowych
    probas = pipe_mlp.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    
    # Obliczenie współczynników True Positive Rate (TPR) oraz False Positive Rate (FPR)
    fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1)
    
    # Interpolacja wyników na średnią krzywą ROC
    mean_tpr = np.interp(mean_fpr, fpr, tpr)
    mean_tpr[0] = 0.0
    all_tpr.append(mean_tpr)
    
    # Obliczenie pola pod krzywą ROC (AUC)
    roc_auc = auc(fpr, tpr)
    
    # Wyświetlenie krzywej ROC dla każdego podziału
    plt.plot(fpr, tpr, label=f'ROC fold {i+1} (area = {roc_auc:.2f})')

# Obliczenie średniego True Positive Rate
mean_tpr = np.mean(all_tpr, axis=0)
mean_tpr[-1] = 1.0

# Obliczenie średniego pola pod krzywą ROC (AUC)
mean_auc = auc(mean_fpr, mean_tpr)

# Wyświetlenie średniej krzywej ROC
plt.plot(mean_fpr, mean_tpr, 'k--', label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)

# Wyświetlenie linii referencyjnych
plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='Random guessing (area=0.5)')
plt.plot([0, 0, 1], [0, 1, 1], linestyle=':', color='black', label='Perfect performance (area=1.0)')

# Ustawienia osi i legendy
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

In [ ]:
# Wygenerowanie krzywej uczenia
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_mlp,
                                                        X=X_train,
                                                        y=y_train,
                                                        train_sizes=np.linspace(0.1, 1.0, 10),
                                                        cv=10,
                                                        n_jobs=1)

# Obliczenie średnich i odchyleń standardowych dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej uczenia
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.05])
plt.show()

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

In [ ]:
# Utwórz potok zawierający StandardScaler i MLPClassifier
pipe_mlp = make_pipeline(StandardScaler(), MLPClassifier(random_state=1, max_iter=1000))

# Zdefiniuj zakres parametru alpha do testowania
param_range_alpha = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0]

# Wygeneruj krzywą walidacji dla parametru alpha w MLPClassifier
train_scores, test_scores = validation_curve(estimator=pipe_mlp,
                                             X=X_train,
                                             y=y_train,
                                             param_name='mlpclassifier__alpha',
                                             param_range=param_range_alpha,
                                             cv=10)

# Oblicz średnie dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreśl krzywą walidacji
plt.plot(param_range_alpha, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(param_range_alpha, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')
plt.plot(param_range_alpha, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(param_range_alpha,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xscale('log')
plt.legend(loc='lower right')
plt.xlabel('Parameter alpha')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.05])
plt.title('Validation Curve for MLPClassifier')
plt.show()

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

In [ ]:
# Definicja siatki parametrów do przeszukania
param_grid = {
    'mlpclassifier__hidden_layer_sizes': [(50,), (100,), (50, 50), (100, 100)],  # Przykładowe konfiguracje warstw ukrytych
    'mlpclassifier__activation': ['logistic', 'relu'],  # Funkcje aktywacji
    'mlpclassifier__alpha': [0.0001, 0.001, 0.01],  # Parametr regularyzacji
}

# Utworzenie obiektu GridSearchCV
gs_mlp = GridSearchCV(estimator=pipe_mlp,
                  param_grid=param_grid,
                  scoring='accuracy',  # Wybór metryki oceny
                  cv=5,  # Liczba podziałów walidacji krzyżowej
                  n_jobs=-1)  # Wykorzystanie wszystkich rdzeni procesora

# Uruchomienie procesu przeszukiwania siatki hiperparametrów
gs_mlp.fit(X_train, y_train)

# Wyświetlenie najlepszych parametrów i wyników
print(gs_mlp.best_score_)
print(gs_mlp.best_params_)
0.9294575897063178
{'mlpclassifier__activation': 'logistic', 'mlpclassifier__alpha': 0.01, 'mlpclassifier__hidden_layer_sizes': (100, 100)}
In [ ]:
clf_mlp = gs_mlp.best_estimator_
clf_mlp.fit(X_train, y_train)
print(f'Test accuracy: {clf_mlp.score(X_test, y_test):.3f}')
Test accuracy: 0.920
In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_mlp,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)
0.9172959662288931
{'mlpclassifier__activation': 'logistic', 'mlpclassifier__alpha': 0.0001, 'mlpclassifier__hidden_layer_sizes': (100, 100)}

Zagnieżdżona walidacja krzyżowa z wykorzystaniem GridSearchCV¶

In [ ]:
nested_gs_mlp = GridSearchCV(estimator = pipe_mlp,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_mlp = cross_val_score(nested_gs_mlp, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_mlp):.3f}'
      f' +/- {np.std(nested_scores_mlp):.3f}')
CV accuracy: 0.928 +/- 0.012

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

In [ ]:
# Utworzenie potoku z MLPClassifier
pipe_mlp = MLPClassifier()

# Definicja przestrzeni parametrów do przeszukania
param_dist = {
    'hidden_layer_sizes': [(50,), (100,), (50, 50), (100, 100)],  # Przykładowe konfiguracje warstw ukrytych
    'activation': ['logistic', 'relu'],  # Funkcje aktywacji
    'alpha': [0.0001, 0.001, 0.01],  # Parametr regularyzacji
}

# Inicjalizacja RandomizedSearchCV
rs_mlp = RandomizedSearchCV(
    estimator=pipe_mlp,
    param_distributions=param_dist,
    scoring='accuracy',
    cv=5,
    n_iter=20,
    n_jobs=-1,
    random_state=42
)

# Dopasowanie modelu
rs_mlp.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(rs_mlp.best_score_)
print(rs_mlp.best_params_)
c:\Users\Dawid\anaconda3\Lib\site-packages\joblib\externals\loky\process_executor.py:700: UserWarning: A worker stopped while some jobs were given to the executor. This can be caused by a too short worker timeout or by a memory leak.
  warnings.warn(
0.691569086651054
{'hidden_layer_sizes': (100,), 'alpha': 0.001, 'activation': 'relu'}

LDA¶

In [ ]:
# Inicjalizacja klasyfikatora LDA
lda = LDA()

# Dopasowanie klasyfikatora do danych
lda.fit(X_train_pca, y_train)

# Wizualizacja granic decyzyjnych
plot_decision_regions(X_train_pca, y_train, classifier=lda)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.title('Decision Boundary for LDA')
plt.show()
In [ ]:
plot_decision_regions(X_test_pca, y_test, classifier=lda)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu LDA¶

Łączenie transformatorów i estymatorów w potoku¶

In [ ]:
pipe_lda = make_pipeline(StandardScaler(),
                        LDA())
pipe_lda.fit(X_train, y_train)
y_pred = pipe_lda.predict(X_test)
test_acc_lda = pipe_lda.score(X_test, y_test)
print(f'Test accuracy: {test_acc_lda: .3f}')
Test accuracy:  0.923

Macierz pomyłek¶

In [ ]:
pipe_lda.fit(X_train, y_train)
y_pred = pipe_lda.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[291  35]
 [ 24 412]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_lda = cross_val_score(estimator = pipe_lda,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_lda}')
print(f'CV accuracy scores: {np.mean(scores_lda):.3f}'
      f' +/- {np.std(scores_lda):.3f}')
CV accuracy scores: [0.93770492 0.9442623  0.92459016 0.93442623 0.92786885 0.95737705
 0.92786885 0.91147541 0.92763158 0.92434211]
CV accuracy scores: 0.932 +/- 0.012

Optymalizacja dokładności (precision), wyniku wycofania (recall score), wyniku f1 (F1 score), oraz współczynnika mathhews dla modelu klasyfikacji¶

In [ ]:
pre_val_lda = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_lda}')

rec_val_lda = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_lda}')

f1_val_lda = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_lda}')

mcc_val_lda = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_lda:.3f}')
Precision: 0.9217002237136466
Recall: 0.944954128440367
F1: 0.9331823329558324
MCC: 0.842

Krzywa ROC¶

In [ ]:
# Podział danych do walidacji krzyżowej
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))

# Inicjalizacja wykresu
fig = plt.figure(figsize=(7, 5))
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []

# Dla każdego podziału walidacji krzyżowej
for i, (train, test) in enumerate(cv):
    # Dopasowanie modelu do danych treningowych
    probas = pipe_lda.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    
    # Obliczenie współczynników True Positive Rate (TPR) oraz False Positive Rate (FPR)
    fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1)
    
    # Interpolacja wyników na średnią krzywą ROC
    mean_tpr = np.interp(mean_fpr, fpr, tpr)
    mean_tpr[0] = 0.0
    all_tpr.append(mean_tpr)
    
    # Obliczenie pola pod krzywą ROC (AUC)
    roc_auc = auc(fpr, tpr)
    
    # Wyświetlenie krzywej ROC dla każdego podziału
    plt.plot(fpr, tpr, label=f'ROC fold {i+1} (area = {roc_auc:.2f})')

# Obliczenie średniego True Positive Rate
mean_tpr = np.mean(all_tpr, axis=0)
mean_tpr[-1] = 1.0

# Obliczenie średniego pola pod krzywą ROC (AUC)
mean_auc = auc(mean_fpr, mean_tpr)

# Wyświetlenie średniej krzywej ROC
plt.plot(mean_fpr, mean_tpr, 'k--', label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)

# Wyświetlenie linii referencyjnych
plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='Random guessing (area=0.5)')
plt.plot([0, 0, 1], [0, 1, 1], linestyle=':', color='black', label='Perfect performance (area=1.0)')

# Ustawienia osi i legendy
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

In [ ]:
# Wygenerowanie krzywej uczenia
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_lda,
                                                        X=X_train,
                                                        y=y_train,
                                                        train_sizes=np.linspace(0.1, 1.0, 10),
                                                        cv=10,
                                                        n_jobs=1)

# Obliczenie średnich i odchyleń standardowych dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej uczenia
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.05])
plt.show()

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

In [ ]:
# Zdefiniowanie zakresu parametru do testowania (np. liczba składników)
param_range = [1, 2, 3, 4, 5]  # Możesz dostosować zakres parametru do własnych potrzeb

# Wygenerowanie krzywej walidacji dla parametru w modelu LDA
train_scores, test_scores = validation_curve(estimator=lda,
                                             X=X_train,
                                             y=y_train,
                                             param_name='n_components',  # Parametr do testowania
                                             param_range=param_range,
                                             cv=10)  # Liczba podziałów walidacji krzyżowej

# Obliczenie średnich dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej walidacji
plt.plot(param_range, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')
plt.plot(param_range, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(param_range,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.legend(loc='lower right')
plt.xlabel('Number of components')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.05])
plt.title('Validation Curve for Linear Discriminant Analysis')
plt.show()

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

In [ ]:
# Definicja siatki parametrów do przeszukania
param_grid = {
    'lineardiscriminantanalysis__solver': ['svd', 'lsqr', 'eigen'],  # Wybór algorytmu optymalizacyjnego
    'lineardiscriminantanalysis__shrinkage': [None, 'auto', 0.1, 0.5, 0.9],  # Parametr zwężenia (shrinkage)
    'lineardiscriminantanalysis__n_components': [None, 1, 2, 3]  # Możesz dostosować te parametry
}

# Utworzenie obiektu GridSearchCV
gs_lda = GridSearchCV(estimator=pipe_lda,
                  param_grid=param_grid,
                  scoring='accuracy',  # Wybór metryki oceny
                  cv=5,  # Liczba podziałów walidacji krzyżowej
                  n_jobs=-1)  # Wykorzystanie wszystkich rdzeni procesora

# Uruchomienie procesu przeszukiwania siatki hiperparametrów
gs_lda.fit(X_train, y_train)

# Wyświetlenie najlepszych parametrów i wyników
print(gs_lda.best_score_)
print(gs_lda.best_params_)
In [ ]:
clf_lda = gs_lda.best_estimator_
clf_lda.fit(X_train, y_train)
print(f'Test accuracy: {clf_lda.score(X_test, y_test):.3f}')
Test accuracy: 0.923
In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_lda,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)

Zagnieżdżona walidacja krzyżowa z wykorzystaniem GridSearchCV¶

In [ ]:
nested_gs_lda = GridSearchCV(estimator = pipe_lda,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_lda = cross_val_score(nested_gs_lda, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_lda):.3f}'
      f' +/- {np.std(nested_scores_lda):.3f}')

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

In [ ]:
# Utworzenie potoku dla LDA
pipe_lda = Pipeline([
    ('scaler', StandardScaler()),  # Jeśli potrzebna jest normalizacja danych
    ('lda', LDA())
])

# Definicja przestrzeni parametrów do przeszukania
param_dist = {
    'lda__solver': ['svd', 'lsqr', 'eigen'],  # Wybór algorytmu optymalizacyjnego
    'lda__shrinkage': [None, 'auto'],  # Parametr zwężenia (shrinkage)
    'lda__n_components': [None, 1, 2, 3]  # Liczba komponentów do uwzględnienia
}

# Inicjalizacja RandomizedSearchCV
rs_lda = RandomizedSearchCV(
    estimator=pipe_lda,
    param_distributions=param_dist,
    scoring='accuracy',
    cv=5,
    n_iter=10,
    n_jobs=-1,
    random_state=42
)

# Dopasowanie modelu
rs_lda.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(rs_lda.best_score_)
print(rs_lda.best_params_)

QDA¶

In [ ]:
# Inicjalizacja klasyfikatora QDA
qda = QDA()

# Dopasowanie klasyfikatora do danych
qda.fit(X_train_pca, y_train)

# Wizualizacja granic decyzyjnych
plot_decision_regions(X_train_pca, y_train, classifier=qda)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.title('Decision Boundary for QDA')
plt.show()
In [ ]:
plot_decision_regions(X_test_pca, y_test, classifier=qda)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

Analiza wskaźników oceny wydajności dla modelu QDA¶

Łączenie transformatorów i estymatorów w potoku¶

In [ ]:
pipe_qda = make_pipeline(StandardScaler(),
                        QDA())
pipe_qda.fit(X_train, y_train)
y_pred = pipe_qda.predict(X_test)
test_acc_qda = pipe_qda.score(X_test, y_test)
print(f'Test accuracy: {test_acc_qda: .3f}')
Test accuracy:  0.913

Macierz pomyłek¶

In [ ]:
pipe_qda.fit(X_train, y_train)
y_pred = pipe_qda.predict(X_test)
confmat = confusion_matrix(y_true = y_test, y_pred = y_pred)
print(confmat)
[[303  23]
 [ 43 393]]
In [ ]:
fig, ax = plt.subplots(figsize = (2.5, 2.5))
ax.matshow(confmat, cmap = plt.cm.Blues, alpha = 0.3)
for i in range(confmat.shape[0]):
    for j in range(confmat.shape[1]):
        ax.text(x = j, y = i, s = confmat[i, j],
                va = 'center', ha = 'center')
        
ax.xaxis.set_ticks_position('bottom')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.show()

Walidacja krzyżowa¶

In [ ]:
scores_qda = cross_val_score(estimator = pipe_qda,
                         X = X_train,
                         y = y_train,
                         cv = 10,
                         n_jobs = 1)
print(f'CV accuracy scores: {scores_qda}')
print(f'CV accuracy scores: {np.mean(scores_qda):.3f}'
      f' +/- {np.std(scores_qda):.3f}')
CV accuracy scores: [0.92459016 0.93114754 0.90819672 0.92786885 0.92786885 0.95081967
 0.94098361 0.92459016 0.91447368 0.92763158]
CV accuracy scores: 0.928 +/- 0.011

Optymalizacja dokładności (precision) i wycofania (recall) modelu klasyfikacji¶

In [ ]:
pre_val_qda = precision_score(y_true = y_test, y_pred = y_pred)
print(f'Precision: {pre_val_qda}')

rec_val_qda = recall_score(y_true = y_test, y_pred = y_pred)
print(f'Recall: {rec_val_qda}')

f1_val_qda = f1_score(y_true = y_test, y_pred = y_pred)
print(f'F1: {f1_val_qda}')

mcc_val_qda = matthews_corrcoef(y_true = y_test, y_pred = y_pred)
print(f'MCC: {mcc_val_qda:.3f}')
Precision: 0.9447115384615384
Recall: 0.9013761467889908
F1: 0.9225352112676056
MCC: 0.826

Krzywa ROC¶

In [ ]:
# Podział danych do walidacji krzyżowej
cv = list(StratifiedKFold(n_splits=3).split(X_train, y_train))

# Inicjalizacja wykresu
fig = plt.figure(figsize=(7, 5))
mean_fpr = np.linspace(0, 1, 100)
all_tpr = []

# Dla każdego podziału walidacji krzyżowej
for i, (train, test) in enumerate(cv):
    # Dopasowanie modelu do danych treningowych
    probas = pipe_qda.fit(X_train[train], y_train[train]).predict_proba(X_train[test])
    
    # Obliczenie współczynników True Positive Rate (TPR) oraz False Positive Rate (FPR)
    fpr, tpr, thresholds = roc_curve(y_train[test], probas[:, 1], pos_label=1)
    
    # Interpolacja wyników na średnią krzywą ROC
    mean_tpr = np.interp(mean_fpr, fpr, tpr)
    mean_tpr[0] = 0.0
    all_tpr.append(mean_tpr)
    
    # Obliczenie pola pod krzywą ROC (AUC)
    roc_auc = auc(fpr, tpr)
    
    # Wyświetlenie krzywej ROC dla każdego podziału
    plt.plot(fpr, tpr, label=f'ROC fold {i+1} (area = {roc_auc:.2f})')

# Obliczenie średniego True Positive Rate
mean_tpr = np.mean(all_tpr, axis=0)
mean_tpr[-1] = 1.0

# Obliczenie średniego pola pod krzywą ROC (AUC)
mean_auc = auc(mean_fpr, mean_tpr)

# Wyświetlenie średniej krzywej ROC
plt.plot(mean_fpr, mean_tpr, 'k--', label=f'Mean ROC (area = {mean_auc:.2f})', lw=2)

# Wyświetlenie linii referencyjnych
plt.plot([0, 1], [0, 1], linestyle='--', color=(0.6, 0.6, 0.6), label='Random guessing (area=0.5)')
plt.plot([0, 0, 1], [0, 1, 1], linestyle=':', color='black', label='Perfect performance (area=1.0)')

# Ustawienia osi i legendy
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()

Diagnozowanie problemów z odchyleniem i wariancją za pomnocą krzywych uczenia się¶

In [ ]:
# Wygenerowanie krzywej uczenia
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_qda,
                                                        X=X_train,
                                                        y=y_train,
                                                        train_sizes=np.linspace(0.1, 1.0, 10),
                                                        cv=10,
                                                        n_jobs=1)

# Obliczenie średnich i odchyleń standardowych dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej uczenia
plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')
plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xlabel('Number of training examples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.05])
plt.show()

Rozwiązanie problemu nadmiernego i niedostatecznego dopasowania za pomocą krzywych walidacji¶

In [ ]:
# Zdefiniowanie zakresu parametru reg_param do testowania
param_range_reg_param = [0.0001, 0.001, 0.01, 0.1, 1.0]

# Wygenerowanie krzywej walidacji dla parametru reg_param w QDA
train_scores, test_scores = validation_curve(estimator=pipe_qda,
                                             X=X_train,
                                             y=y_train,
                                             param_name='quadraticdiscriminantanalysis__reg_param',
                                             param_range=param_range_reg_param,
                                             cv=10)

# Obliczenie średnich dokładności dla danych treningowych i testowych
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

# Wykreślenie krzywej walidacji
plt.plot(param_range_reg_param, train_mean,
         color='blue', marker='o',
         markersize=5, label='Training accuracy')
plt.fill_between(param_range_reg_param, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')
plt.plot(param_range_reg_param, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='Validation accuracy')
plt.fill_between(param_range_reg_param,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')
plt.grid()
plt.xscale('log')
plt.legend(loc='lower right')
plt.xlabel('Parameter reg_param')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.05])
plt.title('Validation Curve for QDA')
plt.show()

Dostrajanie modeli uczenia maszynowego poprzez przeszukiwanie siatki (Grid Search)¶

In [ ]:
# Utworzenie potoku zawierającego StandardScaler i QDA
pipe_qda = make_pipeline(StandardScaler(), QDA())

# Definicja siatki parametrów do przeszukania
param_grid = {
    'quadraticdiscriminantanalysis__reg_param': [0.1, 0.5, 1.0]  # Przykładowe wartości dla parametru reg_param
}

# Utworzenie obiektu GridSearchCV
gs_qda = GridSearchCV(estimator=pipe_qda,
                  param_grid=param_grid,
                  scoring='accuracy',  # Wybór metryki oceny
                  cv=5,  # Liczba podziałów walidacji krzyżowej
                  n_jobs=-1)  # Wykorzystanie wszystkich rdzeni procesora

# Uruchomienie procesu przeszukiwania siatki hiperparametrów
gs_qda.fit(X_train, y_train)

# Wyświetlenie najlepszych parametrów i wyników
print(gs_qda.best_score_)
print(gs_qda.best_params_)
0.9288007752563999
{'quadraticdiscriminantanalysis__reg_param': 0.1}
In [ ]:
clf_qda = gs_qda.best_estimator_
clf_qda.fit(X_train, y_train)
print(f'Test accuracy: {clf_qda.score(X_test, y_test):.3f}')
Test accuracy: 0.925
In [ ]:
scorer = make_scorer(f1_score, pos_label = 0)
gs = GridSearchCV(estimator = pipe_qda,
                  param_grid = param_grid,
                  scoring = scorer,
                  cv = 2)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)
0.9157727180786042
{'quadraticdiscriminantanalysis__reg_param': 0.1}

Zagnieżdżona walidacja krzyżowa z wykorzystaniem GridSearchCV¶

In [ ]:
nested_gs_qda = GridSearchCV(estimator = pipe_qda,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)
nested_scores_qda = cross_val_score(nested_gs_qda, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print(f'CV accuracy: {np.mean(nested_scores_qda):.3f}'
      f' +/- {np.std(nested_scores_qda):.3f}')
CV accuracy: 0.929 +/- 0.010

Badanie konfiguracji hiperparametrów za pomocą wyszukiwania losowego (Randomized Search)¶

In [ ]:
# Utworzenie potoku dla QDA
pipe_qda = Pipeline([
    ('scaler', StandardScaler()),  # Jeśli potrzebna jest normalizacja danych
    ('qda', QDA())
])

# Definicja przestrzeni parametrów do przeszukania
param_dist = {
    'qda__reg_param': [0.1, 0.5, 1.0]  # Parametr regularyzacji QDA
    # Dodatkowe parametry QDA można również tutaj uwzględnić
}

# Inicjalizacja RandomizedSearchCV
rs_qda = RandomizedSearchCV(
    estimator=pipe_qda,
    param_distributions=param_dist,
    scoring='accuracy',
    cv=5,
    n_iter=10,
    n_jobs=-1,
    random_state=42
)

# Dopasowanie modelu
rs_qda.fit(X_train, y_train)

# Wyświetlenie najlepszego wyniku i najlepszych parametrów
print(rs_qda.best_score_)
print(rs_qda.best_params_)
0.9288007752563999
{'qda__reg_param': 0.1}
c:\Users\Dawid\anaconda3\Lib\site-packages\sklearn\model_selection\_search.py:307: UserWarning: The total space of parameters 3 is smaller than n_iter=10. Running 3 iterations. For exhaustive searches, use GridSearchCV.
  warnings.warn(

Podsumowanie¶

In [ ]:
klasyfikatory = [
    "Regresja logistyczna",
    "SVM",
    "Drzewo decyzyjne",
    "KNN",
    "Lasy Losowe",
    "Sieci neuronowe",
    "LDA",
    "QDA"
]

dokladnosci = [test_acc_lr, test_acc_svc, test_acc_dt, test_acc_knn, test_acc_forest, test_acc_mlp, test_acc_lda, test_acc_qda]
dokladnosci_CV = [np.mean(scores_lr), np.mean(scores_svc), np.mean(scores_dt), np.mean(scores_knn),
                np.mean(scores_forest), np.mean(scores_mlp), np.mean(scores_lda), np.mean(scores_qda)]
dokladnosci_2 = [pre_val_lr, pre_val_svc, pre_val_dt, pre_val_knn, pre_val_forest, pre_val_mlp, pre_val_lda, pre_val_qda]
recall = [rec_val_lr, rec_val_svc, rec_val_dt, rec_val_knn, rec_val_forest, rec_val_mlp, rec_val_lda, rec_val_qda]
f1 = [f1_val_lr, f1_val_svc, f1_val_dt, f1_val_knn, f1_val_forest, f1_val_mlp, f1_val_lda, f1_val_qda]
mcc = [mcc_val_lr, mcc_val_svc, mcc_val_dt, mcc_val_knn, mcc_val_forest, mcc_val_mlp, mcc_val_lda, mcc_val_qda]

grid_search = [gs_lr.best_score_, gs_svc.best_score_, gs_dt.best_score_, gs_knn.best_score_,
                gs_forest.best_score_, gs_mlp.best_score_, gs_lda.best_score_, gs_qda.best_score_]

nested_grid_seach = [np.mean(nested_scores_lr), np.mean(nested_scores_lr), np.mean(nested_scores_dt), np.mean(nested_scores_knn),
                    np.mean(nested_scores_forest), np.mean(nested_scores_mlp), np.mean(nested_scores_lda), np.mean(nested_scores_qda)]


random_search = [rs_lr.best_score_, rs_svc.best_score_, rs_dt.best_score_, rs_knn.best_score_,
                rs_forest.best_score_, rs_mlp.best_score_, rs_lda.best_score_, rs_qda.best_score_]

# Convert 'klasyfikatory' to a DataFrame
classifier_df = pd.DataFrame({"Klasyfikator": klasyfikatory})

# Create the summary DataFrame
df = pd.DataFrame({
    "Dokładność": dokladnosci,
    "Dokładność CV": dokladnosci_CV,
    "Zoptymalizowana dokładność": dokladnosci_2,
    "Recall": recall,
    "F1 score": f1,
    "MCC": mcc,
    "Grid Search": grid_search,
    "Zagnieżdżony Grid Search": nested_grid_seach,
    "Random Search": random_search
})

# Concatenate the 'classifier_df' and 'df' DataFrames along the columns axis
summary = pd.concat([classifier_df, df], axis=1)
summary
Out[ ]:
Klasyfikator Dokładność Dokładność CV Zoptymalizowana dokładność Recall F1 score MCC Grid Search Zagnieżdżony Grid Search Random Search
0 Regresja logistyczna 0.922572 0.923227 0.939394 0.924312 0.931792 0.842423 0.931428 0.929783 0.931428
1 SVM 0.919948 0.931424 0.933025 0.926606 0.929804 0.836704 0.931424 0.929783 0.931424
2 Drzewo decyzyjne 0.888451 0.888764 0.911007 0.892202 0.901506 0.773169 0.924537 0.924537 0.925523
3 KNN 0.918635 0.920273 0.932870 0.924312 0.928571 0.834112 0.925849 0.923880 0.925849
4 Lasy Losowe 0.925197 0.920925 0.935632 0.933486 0.934558 0.847272 0.927160 0.923550 0.928472
5 Sieci neuronowe 0.925197 0.928142 0.939675 0.928899 0.934256 0.847581 0.929458 0.928143 0.691569
6 LDA 0.922572 0.931755 0.921700 0.944954 0.933182 0.841542 0.931097 0.931097 0.931097
7 QDA 0.913386 0.927817 0.944712 0.901376 0.922535 0.825613 0.928801 0.928801 0.928801

Najlepsze parametry do danych klasyfikatorów¶

In [ ]:
best_estimator_gs = [gs_lr.best_params_, gs_svc.best_params_, gs_dt.best_params_, gs_knn.best_params_,
                gs_forest.best_params_, gs_mlp.best_params_, gs_lda.best_params_, gs_qda.best_params_]

best_estimator_rs = [rs_lr.best_params_, rs_svc.best_params_, rs_dt.best_params_, rs_knn.best_params_,
                rs_forest.best_params_, rs_mlp.best_params_, rs_lda.best_params_, rs_qda.best_params_]

lista = []
parametry = []

for klasyfikator, estim in zip(klasyfikatory, best_estimator_gs):
    for key in estim:
        lista.append(klasyfikator)
        parametry.append(key)

index = [np.array(lista), np.array(parametry)]
results_01 = pd.DataFrame(np.zeros((len(index[0]), 1)), index=index, columns=["Grid Search CV"])


for klasyfikator, dict in zip(klasyfikatory, best_estimator_gs):
    for param, value in dict.items():
        results_01.loc[(klasyfikator, param), "Grid Search CV"] = value
In [ ]:
results_01
Out[ ]:
Grid Search CV
Regresja logistyczna logisticregression__C 10.0
logisticregression__solver sag
SVM svc__C 10.0
svc__kernel linear
Drzewo decyzyjne decisiontreeclassifier__criterion gini
decisiontreeclassifier__max_depth 1
decisiontreeclassifier__min_samples_leaf 1
decisiontreeclassifier__min_samples_split 2
KNN kneighborsclassifier__metric euclidean
kneighborsclassifier__n_neighbors 11
Lasy Losowe randomforestclassifier__bootstrap False
randomforestclassifier__max_depth 5
randomforestclassifier__n_estimators 50
Sieci neuronowe mlpclassifier__activation logistic
mlpclassifier__alpha 0.01
mlpclassifier__hidden_layer_sizes (100, 100)
LDA lineardiscriminantanalysis__n_components None
lineardiscriminantanalysis__shrinkage None
lineardiscriminantanalysis__solver svd
QDA quadraticdiscriminantanalysis__reg_param 0.1
In [ ]:
lista = []
parametry = []
for klasyfikator, estim in zip(klasyfikatory, best_estimator_rs):
    for key in estim:
        lista.append(klasyfikator)
        parametry.append(key)

index = [np.array(lista), np.array(parametry)]
results_02 = pd.DataFrame(np.zeros((len(index[0]), 1)), index=index, columns=["Randomized Search CV"])

for klasyfikator, dict in zip(klasyfikatory, best_estimator_rs):
    for param, value in dict.items():
        results_02.loc[(klasyfikator, param), "Randomized Search CV"] = value
In [ ]:
results_02
Out[ ]:
Randomized Search CV
Regresja logistyczna logisticregression__solver sag
logisticregression__C 10.0
SVM svc__kernel linear
svc__C 10.0
Drzewo decyzyjne decisiontreeclassifier__criterion entropy
decisiontreeclassifier__max_depth 4
decisiontreeclassifier__min_samples_leaf 14
decisiontreeclassifier__min_samples_split 32
KNN kneighborsclassifier__metric minkowski
kneighborsclassifier__n_neighbors 11
Lasy Losowe randomforestclassifier__bootstrap True
randomforestclassifier__max_depth 30
randomforestclassifier__min_samples_leaf 15
randomforestclassifier__min_samples_split 12
randomforestclassifier__n_estimators 171
Sieci neuronowe hidden_layer_sizes (100,)
alpha 0.001
activation relu
LDA lda__solver eigen
lda__shrinkage None
lda__n_components 1
QDA qda__reg_param 0.1

Podsumowanie¶

Widzimy, że wszystkie wyniki dają bardzo wysokie rezultaty, co pokazuje nam, że klasyfikacja została zrealizowana pomyślnie. Najwyższe wyniki dały takie klasyfikatory jak: LDA, regresja logistyczna, lasy losowe, czy sieci neuronowe. Obserwacje zaklasyfikowane $Cammeo$ i $Osmancik$ w bardzo dużym procencie przypadków zostało poprawnie zaklasyfikowane.